//base camera
//based on code from THREE.FirstPersonControls.js and math from Google

var doll = (function (_doll) {

    //function to create cameras
    _doll.cam = function(fov, aspect, near, far) {
        THREE.PerspectiveCamera.call(this);

	this.fov = fov !== undefined ? fov : 50;
	this.aspect = aspect !== undefined ? aspect : 1;
	this.near = near !== undefined ? near : 0.1;
	this.far = far !== undefined ? far : 2000;
        this.qm = new THREE.Quaternion(0,0,0,1);
        this.name = 'Cam';
        this.useQuaternion = true;
        this.pointerlockonly = true;

        this.pitch = 0;
        this.yaw = 0;
        this.roll = 0;

        this.FREE_CAM = 0;
        this.ACTION_CAM = 1;
        this.RPG_CAM = 2;
        this.FLIGHT_CAM = 3;

        this.mode = this.FREE_CAM;

        //first-person free cam no collision; debug cam
        this.free = {
            dollyspeed: 500,
            lookspeed: 1.3,
            forward: false,
            back: false,
            left: false,
            right: false,
            orbit: false
        };

        //third person action follow mode
        this.action = {
            targets: [] //target(s) to follow
        };

        //RPG camera (SRPG, Classic RPG, SLM)
        this.rpg = {
            targets: []
        };

        //Flight camera (mecha, shooter, fairy);
        this.flight = {
            user: null,
            target: null
        };


        //setup controls for FREE_CAM
        //add the keys to the global KEY structure
        KEY.cam_forward = KEY.w;
        KEY.cam_back = KEY.s;
        KEY.cam_left = KEY.a;
        KEY.cam_right = KEY.d;
        KEY.cam_orbit = KEY.q;

        this.lon = 0;
        this.lat = 0;
        this.phi = 0;
        this.theta = 0;
        this.active = true;

        //FIX: get orbit to work properly
        this.orbit = null;
	this.updateProjectionMatrix();
    };

    _doll.cam.prototype = Object.create(THREE.PerspectiveCamera.prototype);

    _doll.cam.prototype.set_mode = function(m) {
        this.mode = m;
    }
    
    _doll.cam.prototype.add_target = function(t) {
        if(this.mode == this.ACTION_CAM) {
            this.action.targets[this.action.targets.length] = t;
        } else if(this.mode == this.RPG_CAM) {
            this.rpg.targets[this.rpg.targets.length] = t;
        } else if(this.mode == this.FLIGHT_CAM) {
            this.flight.target = t;
        }
    }

    _doll.cam.prototype.on_mousedown = function(cam) {
        return function(mouse) {
            if(mouse.button == 1) {
                
            } else if(mouse.button == 3) {
                cam.flight.lock = true;
            }
        }
    };

    _doll.cam.prototype.on_mouseup = function(cam) {
        return function(mouse) {
            if(mouse.button == 1) {
                
            } else if(mouse.button == 3) {
                cam.flight.lock = false;
            }            
        }
    };

    _doll.cam.prototype.on_keydown = function(cam) {
        return function(k) {
            if( k.code == KEY.cam_forward) {
                cam.free.forward = true;
                cam.flight.forward = true;
            }
            else if(k.code == KEY.cam_back) {
                cam.free.back = true;
                cam.flight.back = true;
            }
            else if(k.code == KEY.cam_left) {
                cam.free.left = true;
                cam.flight.left = true;
            }
            else if(k.code == KEY.cam_right) {
                cam.free.right = true;
                cam.flight.right = true;
            }
            else if(k.code == KEY.cam_orbit) {
                this.orbit = 45 * 0.5;
                cam.free.orbit = true;
                cam.flight.orbit = true;
            }
        }
    };

    _doll.cam.prototype.on_keyup = function(cam) {
        return function(k) {
            if( k.code == KEY.cam_forward) {
                cam.free.forward = false;
                cam.flight.forward = false;
            }
            else if(k.code == KEY.cam_back) {
                cam.free.back = false;
                cam.flight.back = false;
            }
            else if(k.code == KEY.cam_left) {
                cam.free.left = false;
                cam.flight.left = false;
            }
            else if(k.code == KEY.cam_right) {
                cam.free.right = false;
                cam.flight.right = false;
            }
            else if(k.code == KEY.cam_orbit) {
                cam.free.orbit = false;
                cam.flight.orbit = false;
            }
        }
    };

    _doll.cam.prototype.on_mouseenter = function(cam) {
        return function() {
            cam.active = true;
        }
    };

    _doll.cam.prototype.on_mouseleave = function(cam) {
        return function() {
            cam.active = false;
        }
    };

    //turn user controls on
    _doll.cam.prototype.control = function() {
        _doll.input.on_mousedown(this.on_mousedown(this));
        _doll.input.on_mouseup(this.on_mouseup(this));
        _doll.input.on_keydown(this.on_keydown(this));
        _doll.input.on_keyup(this.on_keyup(this));
        _doll.input.on_mouseenter(this.on_mouseenter(this));
        _doll.input.on_mouseleave(this.on_mouseleave(this));
    }

    //update every frame
    _doll.cam.prototype.update = function(delta) {
        if(!this.active) {
            return;
        }

        if(this.mode == this.FREE_CAM) {
            this.freecam_update(delta);
        } else if(this.mode == this.ACTION_CAM) {
            this.actioncam_update(delta);
        } else if(this.mode == this.RPG_CAM) {
            this.rpgcam_update(delta);
        } else if(this.mode == this.FLIGHT_CAM) {
            this.flightcam_update(delta);
        }

    }

    //free movement no collision debug camera
    _doll.cam.prototype.freecam_update = function(delta) {
        if(_doll.input.pointerlocked) {
            var mx,my;
            //correct rotations are opposite mouse movements
            mx = -_doll.input.mouse.moveX * 0.002;
            my = -_doll.input.mouse.moveY * 0.002;

            /*
            //interpolate coords
            if(this.oldmx) {
            mx = ms * mx + (1/delta) * this.oldmx;
            my = ms * my + (1/delta) * this.oldmy;
            }
            */
            //var ms = this.free.lookspeed * delta;
            //mx = mx * ms;
            //my = my * ms;
            this.yaw += mx;
            this.pitch += my;
            this.pitch = Math.max( -_doll.HALFPI, Math.min(_doll.HALFPI, this.pitch));

            /*
              this.useQuaternion = false;
              this.rotation = new THREE.Vector3(this.pitch,this.yaw,0);
              this.rotation.x = this.pitch;
              this.rotation.y = this.yaw;
              this.rotation.z = this.roll;
            */  
            //horz movement
            //use yaw to rotate about the y axis
            this.qm.setFromAxisAngle(_doll.YAXIS, this.yaw);
            this.quaternion.copy(this.qm);
            
            //vert movement
            this.qm.setFromAxisAngle(_doll.XAXIS, this.pitch);
            //combine the yaw rotation with the pitch rotation
            this.quaternion.multiply(this.qm);
        }
        else if (!this.pointerlockonly) {
            //use the middle of the screen to indicate mouse movement
            mx = _doll.input.mouse.x - _doll.halfwidth;
            my = _doll.input.mouse.y - _doll.halfheight;
            if(mx != 0 || my != 0) {
                var look = this.free.lookspeed * delta;
                this.lon += mx * look;//horz
	        this.lat -= my * look;//vert
	        this.lat = Math.max( - 85, Math.min( 85, this.lat ) );

                //angles to find point on sphere
	        this.phi = ( 90 - this.lat ) * _doll.RAD;
	        this.theta = this.lon * _doll.RAD;
                
                //calculating the look point on a sphere
	        var lookpoint = this.target;
	        lookpoint.x = this.position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta );
	        lookpoint.y = this.position.y + 100 * Math.cos( this.phi );
	        lookpoint.z = this.position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta );
                this.lookAt(lookpoint);
            }
        }

        /*
          obj.quaternion = new THREE.Quaternion(obj.position.x, obj.position.y, obj.position.z, 1);
          THREE.Quaternion.slerp(this.quaternion, obj.quaternion, this.qm, 0.07);
          this.quaternion = this.qm;
          this.quaternion.normalize;
        */

        if(this.free.forward) {
            this.translateZ(-this.free.dollyspeed * delta);
        }
        
        if(this.free.back) {
            this.translateZ(this.free.dollyspeed * delta);
        }

        if(this.free.left) {
            this.translateX(-this.free.dollyspeed * delta);
        }

        if(this.free.right) {
            this.translateX(this.free.dollyspeed * delta);
        }

        if(this.free.up) {
            this.translateY(this.free.dollyspeed * delta);
        }

        if(this.free.down) {
            this.translateY(this.free.dollyspeed * delta);
        }

        //TODO:use quaternions for this
        if(this.free.orbit) {
            this.orbit += 5.0;

            this.position.x = 1400 * Math.sin( THREE.Math.degToRad( this.orbit ) );
	    this.position.z = 1400 * Math.cos( THREE.Math.degToRad( this.orbit ) );
            this.lookAt(scene.position);
        }
    }

    _doll.cam.prototype.actioncam_update = function(delta) {
        if(this.action.targets.length > 0) {
            var tp = this.action.targets[0].position;
            this.position.copy(tp);
            this.position.y += 300;
            this.position.z += 600;
            this.lookAt(tp);
        }
    }

    _doll.cam.prototype.rpgcam_update = function(delta) {
        if(this.rpg.targets.length > 0) {
            var tp = this.rpg.targets[0].position;
            this.position.copy(tp);
            this.position.y += 300;
            this.position.z += 600;
            this.lookAt(tp);
        }
    }

    _doll.cam.prototype.flightcam_update = function(delta) {
        if(this.flight.user) {
            if(this.orbit) {
                this.position = this.orbit.step();
                this.position.y += 100;
                this.lookAt(this.flight.user.mesh.position);
                
                if(!this.orbit.is_playing()) {
                    this.orbit = null;
                }
                return;
            }
/*
            var mx,my;
            mx = _doll.input.mouse.moveX;
            my = -_doll.input.mouse.moveY;

            var look = 1.0*delta;

            //this.lon += look;
            //this.lat += look;

            //this.lon += mx * look;//horz
	    //this.lat -= my * look;//vert
	    //this.lat = Math.max( - 30, Math.min( 45, this.lat ) );
	    //this.lon = Math.max( - 45, Math.min( 45, this.lon ) );

  */          
            //angles to find point on sphere
	    this.theta = ( 90 - this.lat ) * _doll.RAD;
	    this.phi = this.lon * _doll.RAD;


            //calculating the look point on a sphere
            _doll.orbit(this.position, this.flight.user.mesh.position, 300, this.phi, this.theta);
            //_doll.orbit(this.position, this.flight.user.cursor.position, 600, this.phi, this.theta);
            this.position.y += 100;
            //this.position.x += -25;
            this.lookAt(this.flight.user.mesh.position);
        }
    }

    //experimental controls...
    _doll.cam.prototype.flightcam_update2 = function(delta) {
        if(this.flight.target && this.flight.user) {
            if(this.flight.user.state.locked) {
                return;
            }
            var tp = this.flight.target;
            this.position.copy(this.flight.user.mesh.position);
            this.position.y += 30;
            this.position.z -= 300 * this.flight.user.facing.z;
            this.lookAt(tp);
        }        
    }


    return _doll;
}(doll || {}));




